package com.nd.teamfile.fileexplorer.helper;

import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;

import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.HandlerThread;
import android.os.Message;
import android.provider.MediaStore.Files.FileColumns;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.widget.ImageView;

import com.nd.teamfile.fileexplorer.helper.FileCategoryHelper.FileCategory;
import com.nd.teamfile.fileexplorer.util.FileExplorerUtil;

/**
 * 异步加载文件图标和缩略图，大多是单线程的。
 * 
 * @ClassName FileIconLoader
 * @Function TODO ADD FUNCTION
 * @Reason TODO ADD REASON
 * 
 * @author HuangYK
 * @version v1.3.3
 * @Date 2014-1-24 下午3:13:17
 * 
 * @see
 */
public class FileIconLoader implements Callback {

	private static final String LOADER_THREAD_NAME = "FileIconLoader";

	/**
	 * 消息类型由UI线程本身发送，以表明一些照片需要加载。
	 */
	private static final int MESSAGE_REQUEST_LOADING = 1;

	/**
	 * 消息类型由加载程序线程发送，以表明一些照片已经被加载。
	 */
	private static final int MESSAGE_ICON_LOADED = 2;

	private static final int MICRO_KIND = 3;

	private static abstract class ImageHolder {
		public static final int NEEDED = 0;// 需要执行
		public static final int LOADING = 1;// 正在加载
		public static final int LOADED = 2;// 已经加载
		// 状态
		int state;

		public static ImageHolder create(FileCategory cate) {
			switch (cate) {
			case Apk:
				return new DrawableHolder();
			case Picture:
			case Video:
				return new BitmapHolder();
			default:
				// 不处理
				break;
			}
			return null;
		};

		// 把图片设置到ImageView
		public abstract boolean setImageView(ImageView v);

		// 图片是否为空
		public abstract boolean isNull();

		// 设置图片信息
		public abstract void setImage(Object image);
	}

	private static class BitmapHolder extends ImageHolder {
		SoftReference<Bitmap> bitmapRef;

		@Override
		public boolean setImageView(ImageView v) {
			if (bitmapRef.get() == null)
				return false;
			v.setImageBitmap(bitmapRef.get());
			return true;
		}

		@Override
		public boolean isNull() {
			return bitmapRef == null;
		}

		@Override
		public void setImage(Object image) {
			bitmapRef = image == null ? null : new SoftReference<Bitmap>(
					(Bitmap) image);
		}
	}

	private static class DrawableHolder extends ImageHolder {
		SoftReference<Drawable> drawableRef;

		@Override
		public boolean setImageView(ImageView v) {
			if (drawableRef.get() == null)
				return false;
			v.setImageDrawable(drawableRef.get());
			return true;
		}

		@Override
		public boolean isNull() {
			return drawableRef == null;
		}

		@Override
		public void setImage(Object image) {
			drawableRef = image == null ? null : new SoftReference<Drawable>(
					(Drawable) image);
		}
	}

	/**
	 * 软缓存图像缩略图。key为文件路径
	 */
	private final static ConcurrentHashMap<String, ImageHolder> mImageCache = new ConcurrentHashMap<String, ImageHolder>();

	/**
	 * imageview所对应的ID map. 注意,这个图片的ID，在照片加载请求开始之前可能会改变。
	 */
	private final ConcurrentHashMap<ImageView, FileId> mPendingRequests = new ConcurrentHashMap<ImageView, FileId>();

	/**
	 * 处理程序的消息，发送到UI线程。
	 */
	private final Handler mMainThreadHandler = new Handler(this);

	/**
	 * 线程负责从数据库中加载图片。创建第一个请求。
	 */
	private LoaderThread mLoaderThread;

	/**
	 * 标志位，确保同一时间只发送一次MESSAGE_PHOTOS_NEEDED实例
	 */
	private boolean mLoadingRequested;

	/**
	 * 标志位，表示图像加载暂停。
	 */
	private boolean mPaused;

	private final Context mContext;

	public FileIconLoader(Context context) {
		mContext = context;
	}

	public static class FileId {
		// 文件路径
		public String mPath;
		// 数据库ID
		public long mId;
		// 文件类别
		public FileCategory mCategory;

		public FileId(String path, long id, FileCategory cate) {
			mPath = path;
			mId = id;
			mCategory = cate;
		}
	}

	/**
	 * 加载照片到提供的图像视图。如果照片已经被缓存，它会立即显示。否则发送一个请求从数据库装载的照片。
	 * 
	 * @n<b>函数名称</b> :loadIcon
	 * @brief
	 * @see
	 * @version Ver 1.3.3
	 * @param @param view
	 * @param @param path
	 * @param @param id
	 * @param @param cate
	 * @param @return
	 * @return boolean
	 * @<b>作者</b> : HuangYK
	 * @<b>创建时间</b> : 2014-1-24下午4:19:57
	 */
	public boolean loadIcon(ImageView view, String path, long id,
			FileCategory cate) {
		boolean loaded = loadCachedIcon(view, path, cate);
		if (loaded) {
			mPendingRequests.remove(view);
		} else {
			FileId p = new FileId(path, id, cate);
			mPendingRequests.put(view, p);
			if (!mPaused) {
				// 发送一个请求开始加载照片
				requestLoading();
			}
		}
		return loaded;
	}

	/**
	 * 取消请求
	 * 
	 * @n<b>函数名称</b> :cancelRequest
	 * @brief
	 * @see
	 * @version Ver 1.3.3
	 * @param @param view
	 * @return void
	 * @<b>作者</b> : HuangYK
	 * @<b>创建时间</b> : 2014-1-24下午4:22:16
	 */
	public void cancelRequest(ImageView view) {
		mPendingRequests.remove(view);
	}

	/**
	 * 检查照片存在于缓存中。如果是这样,设置视图上的照片,否则设置照片的状态 {@link BitmapHolder#NEEDED}
	 * 
	 * @n<b>函数名称</b> :loadCachedIcon
	 * @brief
	 * @see
	 * @version Ver 1.3.3
	 * @param @param view
	 * @param @param path
	 * @param @param cate
	 * @param @return
	 * @return boolean
	 * @<b>作者</b> : HuangYK
	 * @<b>创建时间</b> : 2014-1-24下午2:40:54
	 */
	private boolean loadCachedIcon(ImageView view, String path,
			FileCategory cate) {
		ImageHolder holder = mImageCache.get(path);

		if (holder == null) {
			holder = ImageHolder.create(cate);
			if (holder == null)
				return false;

			mImageCache.put(path, holder);
		} else if (holder.state == ImageHolder.LOADED) {
			if (holder.isNull()) {
				return true;
			}
			
			// 如果设置成功则返回，如果未能设置imageview意味着软引用被回收,需要重新加载照片。
			if (holder.setImageView(view)) {
				return true;
			}
		}

		holder.state = ImageHolder.NEEDED;
		return false;
	}

	/**
	 * 获取文件在数据库中的ID
	 * 
	 * @n<b>函数名称</b> :getDbId
	 * @brief
	 * @see
	 * @version Ver 1.3.3
	 * @param @param path
	 * @param @param isVideo
	 * @param @return
	 * @return long
	 * @<b>作者</b> : HuangYK
	 * @<b>创建时间</b> : 2014-1-24下午4:28:17
	 */
	public long getDbId(String path, boolean isVideo) {
		String volumeName = "external";
		Uri uri = isVideo ? Video.Media.getContentUri(volumeName)
				: Images.Media.getContentUri(volumeName);
		String selection = FileColumns.DATA + "=?";
		String[] selectionArgs = new String[] { path };
		String[] columns = new String[] { FileColumns._ID, FileColumns.DATA };
		Cursor c = mContext.getContentResolver().query(uri, columns, selection,
				selectionArgs, null);
		if (c == null) {
			return 0;
		}
		long id = 0;
		if (c.moveToNext()) {
			id = c.getLong(0);
		}
		c.close();
		return id;
	}

	/**
	 * 停止加载图片,杀死图像加载程序线程和清除所有缓存。
	 * 
	 * @n<b>函数名称</b> :stop
	 * @brief
	 * @see
	 * @version Ver 1.3.3
	 * @param
	 * @return void
	 * @<b>作者</b> : HuangYK
	 * @<b>创建时间</b> : 2014-1-24下午2:55:35
	 */
	public void stop() {
		pause();
		if (mLoaderThread != null) {
			mLoaderThread.quit();
			mLoaderThread = null;
		}
		clear();
	}

	/**
	 * 清除
	 * 
	 * @n<b>函数名称</b> :clear
	 * @brief
	 * @see
	 * @version Ver 1.3.3
	 * @param
	 * @return void
	 * @<b>作者</b> : HuangYK
	 * @<b>创建时间</b> : 2014-1-27下午6:54:00
	 */
	public void clear() {
		mPendingRequests.clear();
		mImageCache.clear();
	}

	/**
	 * 暂时停止加载
	 * 
	 * @n<b>函数名称</b> :pause
	 * @brief
	 * @see
	 * @version Ver 1.3.3
	 * @param
	 * @return void
	 * @<b>作者</b> : HuangYK
	 * @<b>创建时间</b> : 2014-1-24下午2:55:52
	 */
	public void pause() {
		mPaused = true;
	}

	/**
	 * 继续加载
	 * 
	 * @n<b>函数名称</b> :resume
	 * @brief
	 * @see
	 * @version Ver 1.3.3
	 * @param
	 * @return void
	 * @<b>作者</b> : HuangYK
	 * @<b>创建时间</b> : 2014-1-24下午2:56:02
	 */
	public void resume() {
		mPaused = false;
		if (!mPendingRequests.isEmpty()) {
			requestLoading();
		}
	}

	/**
	 * 批量加载图片。发送一条消息到这个线程本身开始加载图片。如果当前视图包含多个图像视图, 所有这些图像视图将有机会请求各自照片之前执行的请求。
	 * 
	 * @n<b>函数名称</b> :requestLoading
	 * @brief
	 * @see
	 * @version Ver 1.3.3
	 * @param
	 * @return void
	 * @<b>作者</b> : HuangYK
	 * @<b>创建时间</b> : 2014-1-24下午2:56:57
	 */
	private void requestLoading() {
		if (!mLoadingRequested) {
			mLoadingRequested = true;
			mMainThreadHandler.sendEmptyMessage(MESSAGE_REQUEST_LOADING);
		}
	}

	/**
	 * 在主线程请求处理
	 */
	public boolean handleMessage(Message msg) {
		switch (msg.what) {
		case MESSAGE_REQUEST_LOADING: {
			mLoadingRequested = false;
			if (!mPaused) {
				if (mLoaderThread == null) {
					mLoaderThread = new LoaderThread();
					mLoaderThread.start();
				}

				mLoaderThread.requestLoading();
			}
			return true;
		}

		case MESSAGE_ICON_LOADED: {
			if (!mPaused) {
				processLoadedIcons();
			}
			return true;
		}
		}
		return false;
	}

	/**
	 * 越过待加载请求并且显示已经加载的照片。如果某些照片还没有被加载，就发送另外一个请求去加载图片。
	 */
	private void processLoadedIcons() {
		Iterator<ImageView> iterator = mPendingRequests.keySet().iterator();
		while (iterator.hasNext()) {
			ImageView view = iterator.next();
			FileId fileId = mPendingRequests.get(view);
			boolean loaded = loadCachedIcon(view, fileId.mPath,
					fileId.mCategory);
			if (loaded) {
				iterator.remove();
			}
		}

		if (!mPendingRequests.isEmpty()) {
			requestLoading();
		}
	}

	/**
	 * 从数据库加载照片的线程
	 */
	private class LoaderThread extends HandlerThread implements Callback {
		private Handler mLoaderThreadHandler;

		public LoaderThread() {
			super(LOADER_THREAD_NAME);
		}

		/**
		 * 将消息发送到该线程来加载所需的照片。
		 */
		public void requestLoading() {
			if (mLoaderThreadHandler == null) {
				mLoaderThreadHandler = new Handler(getLooper(), this);
			}
			mLoaderThreadHandler.sendEmptyMessage(0);
		}

		/**
		 * 收到上述信息,加载图片,然后发送消息给主线程来处理。
		 * 
		 * @n<b>函数名称</b> :handleMessage
		 * @brief
		 * @see
		 * @version Ver 1.3.3
		 * @param @param msg
		 * @param @return
		 * @<b>作者</b> : HuangYK
		 * @<b>创建时间</b> : 2014-1-24下午2:43:42
		 */
		public boolean handleMessage(Message msg) {
			Iterator<FileId> iterator = mPendingRequests.values().iterator();
			while (iterator.hasNext()) {
				FileId id = iterator.next();
				ImageHolder holder = mImageCache.get(id.mPath);
				if (holder != null && holder.state == ImageHolder.NEEDED) {
					holder.state = ImageHolder.LOADING;
					switch (id.mCategory) {
					case Apk:
						Drawable icon = FileExplorerUtil.getApkIcon(mContext,
								id.mPath);
						holder.setImage(icon);
						break;
					case Picture:
					case Video:
						boolean isVideo = id.mCategory == FileCategory.Video;
						if (id.mId == 0)
							id.mId = getDbId(id.mPath, isVideo);
						if (id.mId == 0) {
							// 从数据库获取信息失败
						}
						holder.setImage(isVideo ? getVideoThumbnail(id.mId)
								: getImageThumbnail(id.mId));
						break;
					default:
						break;
					}

					holder.state = BitmapHolder.LOADED;
					mImageCache.put(id.mPath, holder);
				}
			}

			mMainThreadHandler.sendEmptyMessage(MESSAGE_ICON_LOADED);
			return true;
		}

		private Bitmap getImageThumbnail(long id) {
			return Images.Thumbnails.getThumbnail(
					mContext.getContentResolver(), id, MICRO_KIND, null);
		}

		private Bitmap getVideoThumbnail(long id) {
			return Video.Thumbnails.getThumbnail(mContext.getContentResolver(),
					id, MICRO_KIND, null);
		}
	}
}
